home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Libraries / SAT 2.1.2 / Collision ⁄⁄⁄ ƒ / Collision ⁄⁄⁄.p < prev    next >
Encoding:
Text File  |  1994-07-02  |  34.2 KB  |  1,231 lines  |  [TEXT/PJMM]

  1. {Collision ///}
  2. {}
  3. {This demo demonstrates some alternative ways to use SAT:}
  4. {}
  5. {• The animation is called from the standard event loop (via TransSkel). This slows things down}
  6. {quite a bit (since all other processes are allowed to run), but makes the application background-}
  7. {friendly.}
  8. {• It runs in an ordinary, moveable window. We can, with some effort, do this while still}
  9. {using the fast mode (in which case we would have to restrict window dragging so it stays}
  10. {within the main screen, modify certain global (gSAT.ox ad gSAT.oy), and also limit the}
  11. {horizontal alignment of the window like HyperCard does), but in this demo we just use the}
  12. {slow (safe) mode. }
  13. {• We create sprites from QuickDraw calls instead of cicns.}
  14. {• We use the mask regions of the sprites for better collision detection.}
  15. {• We use a pattern as backkground instead of PICTs.}
  16. {• All the code is in one unit. This might make it less structured, less encapsulated, but I wanted}
  17. {to show you that you don't have to do things exactly the way I do in the other demos.}
  18. {• Using a modified sprite record.}
  19. {• Fixed-point positions}
  20. {}
  21. {However, some variations remain that aren't demonstrated even here:}
  22. {• Calculating the positions of the sprites with the system clock (TickCount or Time Manager) instead of}
  23. {moving them each frame. That approach has some advantages (i.e. constant speed on objects), can easily}
  24. {be used with SAT, but is not as simple.}
  25.  
  26.  
  27. program CollisionIII;
  28.  
  29.     uses
  30.         TransSkel, SAT;
  31.  
  32.     const
  33.         newgameItem = 1;
  34.         clearHighItem = 3;
  35.  
  36.         aboutAlrt = 128;
  37.         fileMenuRes = 128;
  38.         shapeMenuRes = 129;
  39.         theWindRes = 128;
  40.  
  41.         kGameTime = 3600; {60 sekunder}
  42.         kExtraTime = 180; {3 sekunder}
  43.         kLevelBonus = 25;
  44.  
  45.     type
  46.         SettingsRec = record
  47.                 high: Longint;
  48.                 player: string[5];
  49.             end;
  50.         SettingsPtr = ^SettingsRec;
  51.         SettingsHnd = ^SettingsPtr;
  52.     var
  53.         settings: SettingsHnd;
  54.         fileMenu, shapeMenu: MenuHandle;
  55.         gameRunning: Boolean;
  56.         gameStartTime, lastSetStartTime: Longint;
  57.         setCount: integer;
  58.         gMode: integer;
  59.         scoreFace, highFace, lastface: FacePtr;
  60.         myFace, welcomeFace: FacePtr;
  61.         score: Longint;
  62.         bgPat: SATPatHandle;
  63.  
  64.         scaledFace: array[0..31] of FacePtr;
  65.  
  66.     procedure Barf;
  67.     begin
  68.         ReportStr('Something went wrong. Sorry.');
  69.         halt;
  70.     end;
  71.  
  72. {Ljud:}
  73.  
  74. {Konstruera en snd-resurs artificiellt}
  75. {Rutinen bygger en handle med reserverad plats för ljudet, som sedan värdrutinen kan skapa.}
  76.     function CreateSnd (size: longint; var sndH: handle; var dataPek: Ptr): Boolean;
  77.         type
  78.             mySndRec = packed record
  79.                     format: integer;
  80.                     numsynth: integer; {must be 0}
  81. {synth}
  82.                     synthid: integer;{5}
  83.                     synthinit: longint;{0}
  84.  
  85.                     numcom: integer; {must be 1}
  86. {command}
  87.                     command: integer;{ $8051}
  88.                     param1: integer; {0}
  89.                     param2: longint; { $14}
  90. {sound header}
  91.                     dataptr: Ptr;
  92.                     datasize: longint;
  93.                     samplerate: longint; {22kHz = $56ee8ba3}
  94.                     loopstart: Ptr;
  95.                     loopend: Ptr;
  96.                     encoding: Byte;{0}
  97.                     basenote: Byte; { $3c}
  98. {data}
  99.                     ljud: packed array[0..0] of Byte;
  100.                 end;
  101.             msrp = ^mySndRec;
  102.             msrh = ^msrp;
  103.         var
  104.             h: msrh;
  105.     begin
  106.         h := msrh(NewHandle(sizeof(mySndRec) + size));
  107.         if h = nil then
  108.             CreateSnd := false
  109.         else
  110.             begin
  111.                 HLock(Handle(h)); {Fixar detta buggen med att ljuden ändras?}
  112.                 with h^^ do
  113.                     begin
  114.                         format := 1;
  115.                         numsynth := 1;
  116.                         synthid := 5;
  117.                         synthinit := 0;
  118.                         numcom := 1;
  119.                         command := $8051;
  120.                         param1 := 0;
  121.                         param2 := $14;
  122.                         dataptr := @ljud[0];
  123.                         datasize := size;
  124.                         samplerate := $56ee8ba3; {div 2 - fast varför köra 11kHz när man synthar?!}
  125.                         loopstart := dataptr;
  126.                         loopend := dataptr; {?}
  127.                         encoding := 0;
  128.                         basenote := $3c;
  129.                         dataPek := dataptr; {Utdata}
  130.                     end; {with}
  131.                 SndH := handle(h);{utdata}
  132.                 CreateSnd := true;
  133.             end; {if nil else}
  134.     end;{CreateSnd}
  135.  
  136.     var
  137.         pushH, bippH, baeH: Handle;
  138.  
  139. {Fixa några bra subrutiner för ljudsyntning?!}
  140. {- Eko}
  141. {- Lågpass och högpass}
  142. {- Sampla upp eller ner?}
  143. {- Frekvensvariation?}
  144. {- Fade in, fade out (mm envelope)}
  145. {Drömmen är förstås FFT, så man kan göra riktigt vass bandspärr, frekvensskift mm.}
  146. {Apropå: kan man inte göra bra ljudkompression med FFT?}
  147.  
  148. {$PUSH}
  149. {$R-}
  150.  
  151. {Rutinen som skall bygga de syntetiska ljud vi önskar!}
  152.     procedure Synth;
  153.         type
  154.             ArtRec = record
  155.                     arr: packed array[0..10000] of Byte;
  156.                 end;
  157.             ArtPtr = ^ArtRec;
  158.         var
  159.             tmpPtr: ArtPtr;
  160.             i: integer;
  161.         const
  162.             pushSize = 3479;
  163.             bippSize = 2959;
  164.             baeSize = 20000;
  165.     begin
  166.         if not CreateSnd(pushSize + 1, pushH, Ptr(tmpptr)) then
  167.             CheckNoMem(nil); {EmergencyExit}
  168.         for i := 0 to pushSize do
  169.             tmpptr^.arr[i] := band(char(random), 127) * (pushSize - i) div pushSize + 128;
  170.         for i := 0 to pushSize - 3 do
  171.             tmpptr^.arr[i] := (tmpptr^.arr[i] + tmpptr^.arr[i + 1] + tmpptr^.arr[i + 2] + tmpptr^.arr[i + 3]) div 4;
  172.         for i := 0 to 64 do
  173.             begin
  174. {tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;}
  175.                 tmpptr^.arr[pushSize - i] := tmpptr^.arr[pushSize - i] * i div 64;
  176.             end;
  177.  
  178.         if not CreateSnd(bippSize + 1, bippH, Ptr(tmpptr)) then
  179.             CheckNoMem(nil); {EmergencyExit}
  180.         for i := 0 to bippSize do
  181.             tmpptr^.arr[i] := i mod (i div 171 + 1) mod 127 + 128; {mjiioo}
  182. {tmpptr^.arr[i] := i mod (i div 171 + 1) + 128; {mjiioo}
  183. {tmpptr^.arr[i] := i mod (i div 17 + 1) + 128; {maipp}
  184. {tmpptr^.arr[i] := band(i, 63) + 128;}
  185.         for i := 0 to 64 do
  186.             begin
  187.                 tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;
  188.                 tmpptr^.arr[bippSize - i] := tmpptr^.arr[bippSize - i] * i div 64;
  189.             end;
  190.  
  191.         if not CreateSnd(baeSize + 1, baeH, Ptr(tmpptr)) then
  192.             CheckNoMem(nil); {EmergencyExit}
  193.         for i := 0 to baeSize do
  194.             tmpptr^.arr[i] := (i div 5) mod (i div 1571 + 1) mod 127 + 128; {mjiioo}
  195. {tmpptr^.arr[i] := i mod (i div 171 + 1) + 128; {mjiioo}
  196. {tmpptr^.arr[i] := i mod (i div 17 + 1) + 128; {maipp}
  197. {tmpptr^.arr[i] := band(i, 63) + 128;}
  198.         for i := 0 to baeSize - 3 do
  199.             tmpptr^.arr[i] := (tmpptr^.arr[i] + tmpptr^.arr[i + 1] + tmpptr^.arr[i + 2] + tmpptr^.arr[i + 3]) div 4;
  200.         for i := 0 to 64 do
  201.             begin
  202.                 tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;
  203.                 tmpptr^.arr[baeSize - i] := tmpptr^.arr[baeSize - i] * i div 64;
  204.             end;
  205.     end;
  206. {$POP}
  207.  
  208.     procedure DoAbout;
  209.     begin
  210.         if 1 = Alert(aboutAlrt, nil) then
  211.             ;
  212.     end;
  213.  
  214. {Two handly routines from my dialog utilities unit.}
  215.     procedure SetTextDItem (theDialog: DialogPtr; itemNo: integer; theString: Str255);
  216.         var
  217.             kind: integer;
  218.             item: ControlHandle;
  219.             box: Rect;
  220.     begin
  221.         GetDItem(theDialog, itemNo, kind, Handle(item), box);
  222. {Check kind}
  223.         kind := BitAnd(kind, 127);
  224.         case kind of
  225.             8, 16: {statText, editText}
  226.                 SetIText(handle(item), theString);
  227.             0, 1, 2, 4, 5, 6: {button, checkbox, radio - men vad är 4?}
  228.                 SetCTitle(item, theString);
  229.             otherwise {Övriga har ingen text man kan sätta}
  230.                 SysBeep(1);
  231.         end;{case}
  232.     end;
  233.     function GetTextDItem (theDialog: DialogPtr; itemNo: integer): Str255;
  234.         var
  235.             kind: integer;
  236.             item: ControlHandle;
  237.             box: Rect;
  238.             tmpStr: Str255;
  239.     begin
  240.         GetDItem(theDialog, itemNo, kind, Handle(item), box);
  241. {Check kind}
  242.         kind := BitAnd(kind, 127);
  243.         tmpStr := '';
  244.         case kind of
  245.             8, 16: {statText, editText}
  246.                 GetIText(handle(item), tmpStr);
  247.             0, 1, 2, 4, 5, 6: {button, checkbox, radio…?}
  248.                 GetCTitle(item, tmpStr);
  249.             otherwise {Övriga har ingen text man kan sätta}
  250.                 SysBeep(1);
  251.         end;{case}
  252.         GetTextDItem := tmpStr;
  253.     end;
  254.     function MyNumToString (l: longint): Str255;
  255.         var
  256.             tmpStr: Str255;
  257.     begin
  258.         NumToString(l, tmpStr);
  259.         MyNumToString := tmpStr;
  260.     end;
  261.  
  262. {Make the new high score dialog}
  263.     procedure AskHigh;
  264.         const
  265.             highDlogID = 129;
  266.         var
  267.             dialog: DialogPtr;
  268.             oldPort: GrafPtr;
  269.             itemHit: integer;
  270.             str: str255;
  271.     begin
  272.         GetPort(oldPort);
  273.         dialog := GetNewDialog(highDlogID, nil, WindowPtr(-1));
  274.         ShowWindow(dialog);
  275.         SelectWindow(dialog);
  276.         SetPort(dialog);
  277.  
  278.         SetTextDItem(dialog, 3, settings^^.player);
  279.         SelIText(dialog, 3, 0, 32767);
  280.         itemHit := -1;
  281.         while (itemHit <> 1) and (itemHit <> 2) do { 1=ok, 2=cancel }
  282.             ModalDialog(nil, itemHit);
  283.         if itemHit = 1 then
  284.             begin
  285.                 str := GetTextDItem(dialog, 3);
  286.                 if length(str) > 5 then
  287.                     str[0] := char(5); {snabbaste sättet att korta den!}
  288.                 settings^^.player := str;
  289.                 settings^^.high := score;
  290.             end;
  291.         DisposeDialog(dialog);
  292.         SetPort(oldPort);
  293.     end;
  294.  
  295. {Reuseable sprite movement routine, called from all sprite handling routines. Some sprites use this}
  296. {as handling routine.}
  297.     procedure SATBounce (me: SpritePtr);
  298.     begin
  299.         me^.position.h := me^.position.h + me^.speed.h;
  300.         me^.position.v := me^.position.v + me^.speed.v;
  301.         if me^.position.h < 0 then
  302.             me^.speed.h := abs(me^.speed.h);
  303.         if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  304.             me^.speed.h := -abs(me^.speed.h);
  305.         if me^.position.v < 0 then
  306.             me^.speed.v := abs(me^.speed.v);
  307.         if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  308.             me^.speed.v := -abs(me^.speed.v);
  309.     end;
  310.  
  311. {The same but using fixed-point position, as in HandlePlayer}
  312.     procedure SATFixedBounce (me: SpritePtr);
  313.     begin
  314.         me^.fixedPos.h := me^.fixedPos.h + me^.speed.h;
  315.         me^.fixedPos.v := me^.fixedPos.v + me^.speed.v;
  316.  
  317.         me^.position.h := BSR(me^.fixedPos.h, 4); {Shift left 4 steps, i.e. div 16}
  318.         me^.position.v := BSR(me^.fixedPos.v, 4);
  319.  
  320. {Since BSR isn't aritmetic shift, a negative fixedPos will unfortunately result in}
  321. {a very large positive position. This must be accounted for when checking borders}
  322. {- or we could use div, but that is slower.}
  323.  
  324.         if me^.fixedPos.h < 0 then
  325.             begin
  326.                 me^.speed.h := abs(me^.speed.h);
  327.                 me^.position.h := 0;
  328.             end
  329.         else if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  330.             me^.speed.h := -abs(me^.speed.h);
  331.         if me^.fixedPos.v < 0 then
  332.             begin
  333.                 me^.speed.v := abs(me^.speed.v);
  334.                 me^.position.v := 0;
  335.             end
  336.         else if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  337.             me^.speed.v := -abs(me^.speed.v);
  338.     end;
  339.  
  340.  
  341.     procedure HandleTheSprite (me: SpritePtr);
  342.     begin
  343.         if me^.speed.h = 0 then
  344.             me^.speed.h := Rand(32) - Rand(32);
  345.         if me^.speed.v = 0 then
  346.             me^.speed.v := Rand(32) - Rand(32);
  347.         if me^.face = nil then
  348.             begin
  349.                 me^.face := myFace;
  350.                 if me^.face <> nil then
  351.                     me^.hotRect := me^.face^.iconMask.bounds;
  352.             end;
  353.         SATFixedBounce(me);
  354.     end;
  355.  
  356.     procedure RedrawScoreFace;
  357.     begin
  358.         SetPortFace(scoreFace);
  359.         EraseRect(scoreFace^.iconMask.bounds);
  360.         MoveTo(2, 14);
  361.         ForeColor(blackColor);
  362.         DrawString('Score:');
  363.         DrawLong(score);
  364.         ForeColor(whiteColor);
  365.         MoveTo(0, 12);
  366.         DrawString('Score:');
  367.         DrawLong(score);
  368.         ForeColor(blackColor);
  369.         SATSetPortScreen;
  370.         SetPortMask(scoreFace);
  371.         EraseRect(scoreFace^.iconMask.bounds);
  372.         MoveTo(0, 12);
  373.         DrawString('Score:');
  374.         DrawLong(score);
  375.         MoveTo(2, 14);
  376.         DrawString('Score:');
  377.         DrawLong(score);
  378.         SATSetPortScreen;
  379.         ChangedFace(scoreFace);
  380.     end;
  381.  
  382.     procedure RedrawHighFace;
  383.         var
  384.             str: Str255;
  385.     begin
  386.         str := stringof('High score:', MyNumToString(settings^^.high), ' by ', settings^^.player);
  387.  
  388.         SetPortFace(highFace);
  389.         EraseRect(highFace^.iconMask.bounds);
  390.         MoveTo(2, 14);
  391.         ForeColor(blackColor);
  392.         DrawString(str);
  393.         ForeColor(whiteColor);
  394.         MoveTo(0, 12);
  395.         DrawString(str);
  396.         ForeColor(blackColor);
  397.         SATSetPortScreen;
  398.         SetPortMask(highFace);
  399.         EraseRect(highFace^.iconMask.bounds);
  400.         MoveTo(0, 12);
  401.         DrawString(str);
  402.         MoveTo(2, 14);
  403.         DrawString(str);
  404.         SATSetPortScreen;
  405.         ChangedFace(highFace);
  406.     end;
  407.  
  408.     procedure RedrawLastFace;
  409.     begin
  410.         SetPortFace(lastface);
  411.         EraseRect(lastface^.iconMask.bounds);
  412.         MoveTo(2, 14);
  413.         DrawString('Last score:');
  414.         DrawLong(score);
  415.         ForeColor(whiteColor);
  416.         MoveTo(0, 12);
  417.         DrawString('Last score:');
  418.         DrawLong(score);
  419.         ForeColor(blackColor);
  420.         SATSetPortScreen;
  421.         SetPortMask(lastface);
  422.         EraseRect(lastface^.iconMask.bounds);
  423.         MoveTo(0, 12);
  424.         DrawString('Last score:');
  425.         DrawLong(score);
  426.         MoveTo(2, 14);
  427.         DrawString('Last score:');
  428.         DrawLong(score);
  429.         SATSetPortScreen;
  430.         ChangedFace(lastface);
  431.     end;
  432.  
  433.     var
  434.         playerFace: array[0..15] of FacePtr;
  435.         playerSpeed: array[0..15] of Point;
  436.  
  437.  
  438.  
  439.  
  440.  
  441. {Redraw all player faces. This is separated from InitPlayerFaces since it must be called on}
  442. {depth changes.}
  443.     procedure ReDrawPlayerFaces;
  444.         const
  445.             totalAngle = 240;
  446.         var
  447.             i: integer;
  448.             r, r1, r2, ri: Rect;
  449.             reg1, reg2: RgnHandle;
  450.             pol: PolyHandle;
  451.     begin
  452.         SetRect(r, 0, 0, 40, 40); {Total face size}
  453.         SetRect(r1, 0, 0, 38, 38); {Colored part}
  454.         SetRect(ri, 9, 9, 29, 29); {Colored part, inner circle}
  455.         SetRect(r2, 2, 2, 40, 40); {Shadow}
  456.         for i := 0 to 15 do
  457.             begin
  458.                 reg1 := NewRgn;
  459.                 reg2 := NewRgn;
  460.  
  461. {Generate shape}
  462.                 SetPortMask(playerFace[i]);
  463.                 PaintArc(r1, i * 360 div 16 - (360 - totalAngle) div 2, totalAngle);
  464.                 EraseArc(ri, i * 360 div 16 - (360 - totalAngle) div 2, totalAngle); {360-graders-skala}
  465.                 if noErr <> BitMapToRegionGlue(reg1, playerFace[i]^.iconMask) then{}
  466.                     ;
  467.                 CopyRgn(reg1, reg2);
  468.                 OffsetRgn(reg2, 2, 2);
  469.  
  470. {Draw face}
  471.                 SetPortFace(playerFace[i]);
  472.                 EraseRect(playerFace[i]^.iconMask.bounds);
  473.                 ForeColor(blackColor);
  474.                 FillRgn(reg2, black); {black "Shadow"}
  475.                 ForeColor(cyanColor);
  476.                 if gSAT.initDepth > 1 then
  477.                     FillRgn(reg1, black) {If we run in color, fill it completely with cyan}
  478.                 else
  479.                     FillRgn(reg1, ltGray); {If we run in b/w, a gray pattern looks nicer}
  480.                 ForeColor(blueColor);
  481.                 FrameRgn(reg1);
  482.                 ForeColor(blackColor);
  483. {Draw mask}
  484.                 SetPortMask(playerFace[i]);
  485.                 EraseRect(playerFace[i]^.iconMask.bounds);
  486.                 FillRgn(reg1, black);
  487.                 FillRgn(reg2, black);
  488.                 SATSetPortScreen;
  489.                 ChangedFace(playerFace[i]);
  490.  
  491.                 DisposeRgn(reg1);
  492.                 DisposeRgn(reg2);
  493.             end;
  494.     end;
  495.  
  496. {Create all player faces.}
  497.     procedure InitPlayerFaces;
  498.         var
  499.             i: integer;
  500.             r: Rect;
  501.     begin
  502. {We use crude approximations to the sine/cosine functions we really want.}
  503. {A real game might init the table by using sine and cosine for real, but I don't}
  504. {want to make this harder to read than it already is. A real game would also}
  505. {use more than 16 directions, say 32 or even 64.}
  506.  
  507.         SetPt(playerSpeed[6], 0, -6);
  508.         SetPt(playerSpeed[7], 2, -5);
  509.         SetPt(playerSpeed[8], 4, -4);
  510.         SetPt(playerSpeed[9], 5, -2);
  511.         SetPt(playerSpeed[10], 6, 0);
  512.         SetPt(playerSpeed[11], 5, 2);
  513.         SetPt(playerSpeed[12], 4, 4);
  514.         SetPt(playerSpeed[13], 2, 5);
  515.         SetPt(playerSpeed[14], 0, 6);
  516.         SetPt(playerSpeed[15], -2, 5);
  517.         SetPt(playerSpeed[0], -4, 4);
  518.         SetPt(playerSpeed[1], -5, 2);
  519.         SetPt(playerSpeed[2], -6, 0);
  520.         SetPt(playerSpeed[3], -5, -2);
  521.         SetPt(playerSpeed[4], -4, -4);
  522.         SetPt(playerSpeed[5], -2, -5);
  523.  
  524.         SetPt(playerSpeed[6], 0, -32);
  525.         SetPt(playerSpeed[7], 14, -28);
  526.         SetPt(playerSpeed[8], 22, -22);
  527.         SetPt(playerSpeed[9], 28, -14);
  528.         SetPt(playerSpeed[10], 32, 0);
  529.         SetPt(playerSpeed[11], 28, 14);
  530.         SetPt(playerSpeed[12], 22, 22);
  531.         SetPt(playerSpeed[13], 14, 28);
  532.         SetPt(playerSpeed[14], 0, 32);
  533.         SetPt(playerSpeed[15], -14, 28);
  534.         SetPt(playerSpeed[0], -22, 22);
  535.         SetPt(playerSpeed[1], -28, 14);
  536.         SetPt(playerSpeed[2], -32, 0);
  537.         SetPt(playerSpeed[3], -28, -14);
  538.         SetPt(playerSpeed[4], -22, -22);
  539.         SetPt(playerSpeed[5], -14, -28);
  540.  
  541.         SetRect(r, 0, 0, 40, 40); {Total face size}
  542.         for i := 0 to 15 do
  543.             begin
  544.                 playerFace[i] := NewFace(r);
  545.                 ChangedFace(playerFace[i]);
  546.             end;
  547.         RedrawPlayerFaces;
  548.     end;
  549.  
  550.     procedure HandlePlayer (me: SpritePtr);
  551.     begin
  552.         me^.mode := gMode;
  553.         me^.face := playerFace[me^.mode];
  554.  
  555.         me^.fixedPos.h := me^.fixedPos.h + playerSpeed[me^.mode].h;
  556.         me^.fixedPos.v := me^.fixedPos.v + playerSpeed[me^.mode].v;
  557.  
  558.         me^.position.h := BSR(me^.fixedPos.h, 4); {Shift left 4 steps, i.e. div 16}
  559.         me^.position.v := BSR(me^.fixedPos.v, 4);
  560.  
  561.         if me^.fixedPos.h < 0 then
  562.             begin
  563.                 me^.position.h := 0;
  564.                 me^.fixedPos.h := 0;
  565. {gMode := BitAnd(BitAnd(4 - gMode, 15) + 4, 15);}
  566.             end;
  567.         if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  568.             begin
  569.                 me^.position.h := gSAT.offSizeH - me^.hotRect.right;
  570.                 me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  571. {gMode := BitAnd(BitAnd(4 - gMode, 15) + 4, 15);}
  572.             end;
  573.         if me^.fixedPos.v < 0 then
  574.             begin
  575.                 me^.position.v := 0;
  576.                 me^.fixedPos.v := 0;
  577. {gMode := BitAnd(-gMode, 15);}
  578.             end;
  579.         if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  580.             begin
  581.                 me^.position.v := gSAT.offSizeV - me^.hotRect.bottom;
  582.                 me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  583. {gMode := BitAnd(-gMode, 15);}
  584.             end;
  585.     end;
  586.  
  587. {Get a vector from center to center of two sprites}
  588.     function Vector (s1, s2: SpritePtr): Point;
  589.     begin
  590.         Vector.h := s1^.position.h + s1^.face^.iconMask.bounds.right div 2 - s2^.position.h - s2^.face^.iconMask.bounds.right div 2;
  591.         Vector.v := s1^.position.v + s1^.face^.iconMask.bounds.right div 2 - s2^.position.v - s2^.face^.iconMask.bounds.right div 2;
  592.     end;
  593.  
  594. {Squared distance between centers of two sprites}
  595.     function Dist2 (s1, s2: SpritePtr): Longint;
  596.         var
  597.             v: Point;
  598.     begin
  599.         v := Vector(s1, s2);
  600.         Dist2 := v.h * v.h + v.v * v.v;
  601.     end;
  602.  
  603.     procedure HitPlayer (me, him: SpritePtr);
  604.         var
  605.             r1, r2: RgnHandle;
  606.             v: Point;
  607.     begin
  608. {We know that out hotRects coincide. However, that doesn't mean that we must take it as a}
  609. {collision! Rather, we can do more processing here to decide whether or not it was a collision.}
  610. {In this case, we copy the mask regions of each sprite, offset them to the proper positions,}
  611. {and check if they, too, overlap!}
  612. {}
  613. {Do you think we are doing double work, both dealing with hotRects and the regions? If you do,}
  614. {let me explain some more. The idea is that SAT checks the hotRects for you, which takes away}
  615. {next to all false hits. Checking hotRects is *fast*, so that's what we can afford to do all-to-all}
  616. {(or all-to-near, depending on the chosen search mode). Once a *possible* collision is detected,}
  617. {we can spend some time analyzing it further!}
  618.  
  619. {First of all, let's do some error checking. We could also have done this when loading the faces.}
  620. {Most programs won't have to bother whether or not the regions have been generated}
  621. {successfully, but when using them this way, they must exist or we may get a crash.}
  622.         if (me^.face^.maskRgn = nil) or (him^.face^.maskRgn = nil) then
  623.             begin
  624.                 ReportStr('Error: No mask region!');
  625.                 exit(HitPlayer);
  626.             end;
  627.  
  628. {Make copies of the mask regions and offset them to the proper places.}
  629.         r1 := NewRgn;
  630.         r2 := NewRgn;
  631.         CopyRgn(me^.face^.maskRgn, r1);
  632.         CopyRgn(him^.face^.maskRgn, r2);
  633.         OffsetRgn(r1, me^.position.h, me^.position.v);
  634.         OffsetRgn(r2, him^.position.h, him^.position.v);
  635.  
  636.         SectRgn(r1, r2, r1);                    {Is there any overlap?}
  637.  
  638. {If empty, no collision, otherwise, handle the collision!}
  639.         if not EmptyRgn(r1) then
  640.             begin
  641.  
  642.                 if Dist2(me, him) > 60 then
  643.                     begin
  644. {Hit too far out, so let's call it the outside. Bounce away him.}
  645. {We could make more efforts here for a good bounce.}
  646.                         him^.position.h := him^.position.h + me^.speed.h;
  647.                         him^.speed.h := -him^.speed.h + me^.speed.h;
  648.                         him^.position.v := him^.position.v + me^.speed.v;
  649.                         him^.speed.v := -him^.speed.v + me^.speed.v;
  650. {Finally, make sure the other is moving *away* from us!}
  651. {And when we're at it, why not move it just a little, too?}
  652. {Yuck, this is ugly! Yup, careless programming. Hack, hack!}
  653.                         v := Vector(me, him);
  654.                         if v.h > 0 then
  655.                             begin
  656.                                 if him^.speed.h > 0 then
  657.                                     him^.speed.h := -him^.speed.h;
  658.                                 him^.position.h := him^.position.h - 1;
  659.                             end
  660.                         else
  661.                             begin
  662.                                 if v.h < 0 then
  663.                                     if him^.speed.h < 0 then
  664.                                         him^.speed.h := -him^.speed.h;
  665.                                 him^.position.h := him^.position.h + 1;
  666.                             end;
  667.                         if v.v > 0 then
  668.                             begin
  669.                                 if him^.speed.v > 0 then
  670.                                     him^.speed.v := -him^.speed.v;
  671.                                 him^.position.v := him^.position.v - 1;
  672.                             end
  673.                         else
  674.                             begin
  675.                                 if v.v < 0 then
  676.                                     if him^.speed.v < 0 then
  677.                                         him^.speed.v := -him^.speed.v;
  678.                                 him^.position.v := him^.position.v + 1;
  679.                             end;
  680.  
  681.                     end
  682.                 else
  683.                     begin
  684. {This looks like inside! Let's eat him.}
  685.                         score := score + 1;
  686.                         RedrawScoreFace;
  687.                         him^.task := nil;
  688.                         SATSoundPlay(bippH, 1, true);
  689.                     end; {Dist2}
  690.             end; {not EmptyRgn}
  691.  
  692.         DisposeRgn(r1);
  693.         DisposeRgn(r2);
  694.     end;
  695.  
  696.     procedure InitScoreFace;
  697.         var
  698.             r: Rect;
  699.     begin
  700.         SetRect(r, 0, 0, 80, 14);{}
  701.         scoreFace := NewFace(r);{NewFace gör "Application zone damaged!" Det är inte bredden.}
  702.         ChangedFace(scoreFace);
  703. {RedrawScoreFace; {MEN MED DENNA blev det inga problem!!!}
  704. {Slutsats: NewFace lämnar nånting i fel skick, men ChangedFace fixar det!}
  705.         SetRect(r, 0, 0, 200, 16);{}
  706.         highFace := NewFace(r);{NewFace gör "Application zone damaged!" Det är inte bredden.}
  707.         ChangedFace(highFace);
  708.         SetRect(r, 0, 0, 120, 16);{}
  709.         lastFace := NewFace(r);{NewFace gör "Application zone damaged!" Det är inte bredden.}
  710.         ChangedFace(lastFace);
  711. {RedrawHighFace; {MEN MED DENNA blev det inga problem!!!}
  712.     end;
  713.  
  714.     procedure SetupDummy (me: SpritePtr);
  715.     begin
  716.         me^.task := @SATBounce;
  717.     end;
  718.  
  719.     procedure SetupSmall (me: SpritePtr);
  720.     begin
  721.         me^.face := myFace;
  722.         me^.hotRect := me^.face^.iconMask.bounds;
  723.         me^.task := @HandleTheSprite;
  724.  
  725.         me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  726.         me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  727.     end;
  728.  
  729.     procedure SetupPlayer (me: SpritePtr);
  730.     begin
  731.         me^.face := playerFace[0];
  732.         me^.hotRect := me^.face^.iconMask.bounds;
  733.         me^.task := @HandlePlayer;
  734.         me^.hitTask := @HitPlayer;
  735.  
  736.         me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  737.         me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  738.     end;
  739.  
  740.  
  741.     procedure NewSet;
  742.         var
  743.             sp: SpritePtr;
  744.             i: integer;
  745.     begin
  746. {Kill all sprites}
  747.         while gSAT.sRoot <> nil do
  748.             KillSprite(gSAT.sRoot);
  749.  
  750. {Create the pills}
  751.         for i := 1 to 10 do
  752.             sp := NewSprite(-1, Rand(gSAT.offSizeH - 32), Rand(gSAT.offSizeV - 32), @SetupSmall);
  753.         if settings^^.high > 7 then
  754.             for i := 8 to settings^^.high do
  755.                 sp := NewSprite(-1, Rand(gSAT.offSizeH - 32), Rand(gSAT.offSizeV - 32), @SetupSmall);
  756.  
  757.         sp := NewSprite(0, Rand(gSAT.offSizeH - 32), Rand(gSAT.offSizeV - 32), @SetupDummy);
  758.         RedrawScoreFace;
  759.         sp^.face := scoreFace;
  760.         repeat
  761.             sp^.speed.h := Rand(5) - 2
  762.         until sp^.speed.h <> 0;
  763.         repeat
  764.             sp^.speed.v := Rand(3) - 1
  765.         until sp^.speed.v <> 0;
  766.         sp^.hotRect := sp^.face^.iconMask.bounds;
  767. {Hoppsan- fattas nåt!}
  768.  
  769.         sp := NewSprite(1, (gSAT.offSizeH - 32) div 2, (gSAT.offSizeV - 32) div 2, @SetupPlayer);
  770.         gMode := 0;
  771.  
  772.         SATBackChanged(gSAT.bounds);
  773.         setCount := setCount + 1;
  774.         FlushEvents(6, 0); {Glöm klick från förra uppsättningen!}
  775.         if not (TickCount > gameStartTime + kGameTime) then {Om tiden INTE är ute så skall vi ändra!}
  776.             lastSetStartTime := TickCount;
  777.     end;
  778.  
  779. {An example of how you can (with some effort) scale a sprite.}
  780.     procedure ScaleWelcomeFace;
  781.         var
  782.             srcFacePort, destFacePort: GrafPtr;
  783.             i: integer;
  784.             scaleRect: Rect;
  785.     begin
  786. {Get the rectangle of the original}
  787.         scaleRect := welcomeFace^.iconMask.bounds;
  788.  
  789.         for i := 0 to 31 do
  790.             begin
  791. {SetPortFace to the source. This must be done each turn since ChangedFace changes it.}
  792.                 SetPortFace(welcomeFace); {Set the FIRST of SAT's two internal face-ports to the original face.}
  793.                 GetPort(srcFacePort); {Get the port.}
  794. {Modify the size}
  795.                 scaleRect.bottom := scaleRect.bottom - 2;
  796.                 scaleRect.right := scaleRect.right - 2;
  797. {Create the new face}
  798.                 if scaledFace[i] = nil then
  799.                     scaledFace[i] := NewFace(scaleRect);
  800. {Get a port to it}
  801.                 SetPortFace2(scaledFace[i]); {Set the SECOND of SAT's two internal face ports to the new face.}
  802.                 GetPort(destFacePort); {Get the port.}
  803. {Copy the image}
  804.                 CopyBits(srcFacePort^.portBits, destFacePort^.portBits, welcomeFace^.iconMask.bounds, scaleRect, srcCopy + ditherCopy, nil);
  805.                 CopyBits(welcomeFace^.iconMask, scaledFace[i]^.iconMask, welcomeFace^.iconMask.bounds, scaleRect, srcCopy, nil);
  806.                 ChangedFace(scaledFace[i]); {Done changing it. Tell SAT that it may do whatever it needs.}
  807.             end; {for}
  808.     end; {ScaleWelcomeFace}
  809.  
  810.     procedure WindUpdate (whatever: Boolean);
  811.         var
  812.             savePort: GrafPtr;
  813.             saveDev: GDHandle;
  814.     begin
  815.         if SATDepthChangeTest then
  816. {IMPORTANT! We must redraw all internally generated faces on depth changes!}
  817.             begin
  818.                 ReDrawPlayerFaces;
  819.                 RedrawScoreFace;
  820.                 RedrawHighFace;
  821.                 RedrawLastFace;
  822.                 ScaleWelcomeFace;
  823.  
  824. {We also have to redraw the background, since it's not a PICT (in which case that is automatic)}
  825.                 GetPort(savePort);
  826.                 if colorFlag then
  827.                     saveDev := GetGDevice;
  828.                 SATSetPortBackScreen;
  829.                 SATPenPat(bgPat);
  830.                 PaintRect(gSAT.backScreen^.portRect);
  831.                 PenNormal;
  832.                 CopyBits(gSAT.backScreen^.portBits, gSAT.offScreen^.portBits, gSAT.offScreen^.portRect, gSAT.offScreen^.portRect, srcCopy, nil);
  833.                 SetPort(savePort);
  834.                 if colorFlag then
  835.                     SetGDevice(saveDev);
  836.  
  837.             end;
  838.         PeekOffscreen;
  839.     end;
  840.  
  841.     procedure WindClose;
  842.     begin
  843.         SkelWhoa;
  844.     end;
  845.  
  846.     procedure WindMouse (where: Point; when: Longint; modifiers: integer);
  847.         var
  848.             found, sp: SpritePtr;
  849.             anyLeft: Boolean;
  850.             myRegion: RgnHandle;
  851.     begin
  852. {Not needed for the game, but note that we can check the mask region of a sprite}
  853. {towards a mouse click as well as a colliding sprite! For demonstrating this, mouse}
  854. {clicks are processed, and if a sprite is hit, a SysBeep is made. Try this by clicking}
  855. {in and around the "Hello" sprite!}
  856.  
  857.         myRegion := NewRgn;
  858.         sp := gSAT.sRoot;
  859.         found := nil;
  860.         while sp <> nil do                                        {Search through the sprite list}
  861.             begin
  862.                 if PtInRect(where, sp^.r) then                        {We are in the rect!}
  863.                     if sp^.face <> nil then                            {Does it have a face at all? Remember it's legal not to have one!}
  864.                         if sp^.face^.maskRgn <> nil then                {Does that face have a mask region? It should, but…}
  865.                             begin
  866.                                 CopyRgn(sp^.face^.maskRgn, myRegion);    {Copy the mask region}
  867.                                 OffsetRgn(myRegion, sp^.position.h, sp^.position.v);        {Offset it to the position of the sprite}
  868.                                 if PtInRgn(where, myRegion) then            {Are we in the region?}
  869.                                     found := sp;                                    {Yes!}
  870.                             end;
  871.                 sp := sp^.next;                                        {Next sprite…}
  872.             end;
  873.         if found <> nil then
  874.             SysBeep(1);                                            {We hit something. Tell us so.}
  875.         DisposeRgn(myRegion);
  876.     end;
  877.  
  878.  
  879.     procedure WindKey (theKey: char; theMods: integer);
  880.     begin
  881. {Hard-coded keys; real games have customizable keys.}
  882.         case theKey of
  883.             ',', 'z', '1': 
  884.                 gMode := BitAnd(gMode - 1, 15);
  885.             '.', 'x', '2': 
  886.                 gMode := BitAnd(gMode + 1, 15);
  887.             otherwise
  888.         end; {case}
  889.         ObscureCursor; {Hide the cursor until the mouse is moved.}
  890.     end;
  891.  
  892.     procedure SetupSAT (theWind: WindowPtr);{Calls CustomInitSAT and paints the background with a pattern}
  893.         var
  894.             saveport: GrafPtr;
  895.             savedev: GDHandle;
  896.     begin
  897.         CustomInitSAT(0, 0, theWind^.portRect, theWind, nil, false, false, false, true, false); {Nytt försök!}
  898.  
  899. {We use a customized sprite record! Thus, we must SetSpriteSize before creating sprites!}
  900.         SATSetSpriteRecSize(sizeof(Sprite));
  901.  
  902.         if bgPat = nil then
  903.             bgPat := SATGetPat(128);
  904.         if bgPat = nil then
  905.             Barf;
  906.  
  907.         SATGetPort(savePort, saveDev);
  908.  
  909.         SATSetPortBackScreen;
  910.         SATPenPat(bgPat);
  911.         PaintRect(gSAT.backScreen^.portRect);
  912.         PenNormal;
  913.         CopyBits(gSAT.backScreen^.portBits, gSAT.offScreen^.portBits, gSAT.offScreen^.portRect, gSAT.offScreen^.portRect, srcCopy, nil);
  914.  
  915.         SATSetPort(savePort, saveDev);
  916.         CopyBits(gSAT.backScreen^.portBits, gSAT.wind^.portBits, gSAT.wind^.portRect, gSAT.wind^.portRect, srcCopy, nil);
  917.  
  918.         if SkelWindow(theWind, @WindMouse, @WindKey, @WindUpdate, nil, @WindClose, nil, nil, false) then
  919.             ;
  920.     end;
  921.  
  922.     procedure SetupWindow;
  923.         var
  924.             slaskWind, theWind: WindowPtr;
  925.             tmpWorld: SysEnvRec;
  926.             tmpCol: Boolean;
  927. {r: Rect;}
  928. {peek: WindowPeek;}
  929.     begin
  930.         tmpCol := false;
  931.         if noErr = SysEnvirons(1, tmpWorld) then
  932.             tmpCol := tmpWorld.hasColorQD;
  933.  
  934.         if tmpCol then
  935.             theWind := GetNewCWindow(theWindRes, nil, WindowPtr(-1))
  936.         else
  937.             theWind := GetNewWindow(theWindRes, nil, WindowPtr(-1));
  938.  
  939. {peek := WindowPeek(theWind);}
  940.  
  941.         if theWind = nil then
  942.             Barf;
  943.  
  944. {MoveWindow(theWind, 50, 50, false);}
  945.  
  946. {r := WindowPeek(theWind)^.contRgn^^.rgnBBox;}
  947.  
  948. {slaskWind := theWind;}
  949.  
  950.         SetupSAT(theWind); {Calls CustomInitSAT and paints the background with a pattern}
  951.  
  952.         ShowWindow(gSAT.wind);
  953.         SelectWindow(gSAT.wind);
  954.         SetPort(gSAT.wind);
  955.         PeekOffScreen;
  956.     end;
  957.  
  958. {The task the welcome sprite has while zooming.}
  959.     procedure ZoomWelcome (me: SpritePtr);
  960.     begin
  961.         me^.mode := me^.mode + 1;
  962. {Compensate for the size change to make it centered in one place.}
  963.         me^.position.h := me^.position.h - 1;
  964.         me^.position.v := me^.position.v - 1;
  965.         if me^.mode >= 32 then
  966.             begin
  967.                 me^.face := welcomeFace;
  968.                 me^.task := @SATBounce;
  969.             end
  970.         else
  971.             me^.face := scaledFace[32 - me^.mode];
  972.     end;
  973.  
  974. {Initialize faces.}
  975.     procedure InitSpriteFaces;
  976.         var
  977.             i: integer;
  978.     begin
  979.         myFace := GetFace(128);
  980.         if myFace = nil then
  981.             Barf;
  982.         welcomeFace := GetFace(138);
  983.         if welcomeFace = nil then
  984.             Barf;
  985. {We don't HAVE to bail out when a face fails to load - the program will stll wor, but that face will}
  986. {not be visible.}
  987.         ScaleWelcomeFace;
  988.     end;
  989.  
  990.     var
  991.         lastTime: Longint;
  992.  
  993. {DirtyWork is called from TransSkel}
  994.     procedure DirtyWork;
  995.         var
  996.             sp: SpritePtr;
  997.             ph: PicHandle;
  998.             r: Rect;
  999.             savePort: GrafPtr;
  1000.             saveDev: GDHandle;
  1001.     begin
  1002. {We can check TickCount as usual, since we never know how often we get null events.}
  1003.         if lastTime + 1 < TickCount then
  1004.             begin
  1005.                 RunSAT(false);
  1006.                 lastTime := tickCount;
  1007.             end;
  1008.  
  1009.         if gameRunning then
  1010.             begin
  1011. {Timebar}
  1012.                 GetPort(savePort);
  1013.                 if colorFlag then
  1014.                     saveDev := GetGDevice;
  1015.                 SATSetPortBackScreen;
  1016. {I *should* change only the part that actually changes!}
  1017.                 r := gSAT.wind^.portRect;
  1018.                 SATBackChanged(r);
  1019.                 r.right := 5;
  1020.                 r.top := r.bottom * (lastSetStartTime + kGameTime - TickCount) div kGameTime;
  1021.                 ForeColor(redColor); {Quickest way to get a color.}
  1022.                 PaintRect(r);
  1023.                 r.bottom := r.top;
  1024.                 r.top := 0;
  1025.                 SATPenPat(bgPat);
  1026.                 PaintRect(r);
  1027.                 PenNormal;
  1028.  
  1029.                 SetPort(savePort);
  1030.                 if colorFlag then
  1031.                     SetGDevice(saveDev);
  1032. {end of Timebar}
  1033.  
  1034.                 if TickCount > lastSetStartTime + kGameTime then
  1035.                     begin
  1036.                         SATSoundPlay(baeH, 5, true);
  1037. {NewSet;}
  1038.  
  1039.                         if TickCount > gameStartTime + kGameTime then
  1040.                             begin
  1041.                                 if score > settings^^.high then
  1042.                                     begin
  1043. {settings^^.high := score;}
  1044.                                         SATSoundEvents;
  1045.                                         AskHigh;
  1046. {ReportStr(stringof('New highscore: ', score, ' points! Whoopie!'));}
  1047.                                         ChangedResource(Handle(settings));
  1048.                                     end;
  1049.  
  1050. {Kill all sprites}
  1051.                                 while gSAT.sRoot <> nil do
  1052.                                     KillSprite(gSAT.sRoot);
  1053.  
  1054.                                 RedrawHighFace;
  1055.                                 RedrawLastFace;
  1056. {Time for breaking some of my conventions! The stuff below should be done in "setup" and "handle"}
  1057. {routines, as recommened in the manual and done in other demos - but if we want to mess up the code,}
  1058. {we are free to do so! The sprites below set up their faces and speeds right here, and share a common}
  1059. {handling routine (SATBounce).}
  1060.  
  1061. {Make the "hello" sprite}
  1062.                                 sp := NewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 2, @SetupDummy);
  1063.                                 sp^.face := welcomeFace;
  1064.                                 repeat
  1065.                                     sp^.speed.h := Rand(3) - 1
  1066.                                 until sp^.speed.h <> 0;
  1067.                                 repeat
  1068.                                     sp^.speed.v := Rand(3) - 1
  1069.                                 until sp^.speed.v <> 0;
  1070.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1071.                                 sp^.task := @ZoomWelcome;
  1072. {High score sprite}
  1073.                                 sp := NewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 4, @SetupDummy);
  1074.                                 sp^.face := highFace;
  1075.                                 repeat
  1076.                                     sp^.speed.h := Rand(7) - 3
  1077.                                 until sp^.speed.h <> 0;
  1078.                                 repeat
  1079.                                     sp^.speed.v := Rand(3) - 1
  1080.                                 until sp^.speed.v <> 0;
  1081.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1082. {Last score sprite}
  1083.                                 sp := NewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 3, @SetupDummy);
  1084.                                 sp^.face := lastFace;
  1085.                                 repeat
  1086.                                     sp^.speed.h := Rand(7) - 3
  1087.                                 until sp^.speed.h <> 0;
  1088.                                 repeat
  1089.                                     sp^.speed.v := Rand(3) - 1
  1090.                                 until sp^.speed.v <> 0;
  1091.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1092.  
  1093.                                 SATSetPortScreen;
  1094.                                 PeekOffscreen; {Just to make sure killed sprites are erased}
  1095.                                 gameRunning := false;
  1096.                             end;
  1097.  
  1098.                     end;
  1099.             end;
  1100.  
  1101.         if not gameRunning then
  1102.             if gSAT.sRoot = nil then
  1103.                 begin
  1104. {Messy code for setting up the "hello" sprite - which is why I recommend the use of setup routines.}
  1105.                     sp := NewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 2, @SetupDummy);
  1106.                     sp^.face := welcomeFace;
  1107.                     repeat
  1108.                         sp^.speed.h := Rand(3) - 1
  1109.                     until sp^.speed.h <> 0;
  1110.                     repeat
  1111.                         sp^.speed.v := Rand(3) - 1
  1112.                     until sp^.speed.v <> 0;
  1113.                     sp^.hotRect := sp^.face^.iconMask.bounds;
  1114.                     sp^.task := @ZoomWelcome;
  1115.                 end;
  1116.     end;
  1117.  
  1118.     procedure InitHigh;
  1119.     begin
  1120.         settings := SettingsHnd(GetResource('Sett', 0));
  1121.         if settings = nil then {Didn't exist - create it!}
  1122.             begin
  1123.                 settings := SettingsHnd(NewHandle(Sizeof(SettingsRec)));
  1124.                 if settings = nil then
  1125.                     begin
  1126.                         SysBeep(1);
  1127.                         halt;
  1128.                     end;
  1129.                 settings^^.high := 0;
  1130.                 AddResource(handle(settings), 'Sett', 0, '');
  1131.             end
  1132.         else {Did exist - check the size!}
  1133.             if GetHandleSize(Handle(settings)) < sizeof(SettingsRec) then
  1134.                 SetHandleSize(Handle(settings), sizeof(SettingsRec));
  1135.     end;
  1136.  
  1137.     procedure DoFileMenu (item: integer);
  1138.     begin
  1139.         case item of
  1140.             newGameItem: 
  1141.                 begin
  1142.                     score := 0;
  1143.                     gameRunning := true;
  1144.                     gameStartTime := TickCount;
  1145.                     lastSetStartTime := TickCount;
  1146.                     setCount := 0;
  1147.                     NewSet;
  1148.  
  1149.                     ObscureCursor; {Hide the cursor until the mouse is moved.}
  1150.                 end;
  1151.             clearHighItem: 
  1152.                 if QuestionStr('Set the high score to zero?') then
  1153.                     begin
  1154.                         settings^^.high := 0;
  1155.                         ChangedResource(handle(settings));
  1156.                     end;
  1157.             otherwise
  1158.                 SkelWhoa;
  1159.         end;
  1160.     end;
  1161.  
  1162.     procedure DoShapeMenu (item: integer);
  1163.         const
  1164.             wide = 1;
  1165.             tall = 2;
  1166.         var
  1167.             p: Point;
  1168.     begin
  1169.         p := gSAT.wind^.portRect.botRight;
  1170.         case item of
  1171.             wide: 
  1172.                 if gSAT.wind^.portRect.bottom > gSAT.wind^.portRect.right then
  1173.                     begin
  1174.                         CheckItem(shapeMenu, wide, true);
  1175.                         CheckItem(shapeMenu, tall, false);
  1176.                         SizeWindow(gSAT.wind, p.v, p.h, false); {swap size}
  1177.                         KillSAT;
  1178.                         SetupSAT(gSAT.wind);
  1179.                         gameRunning := false;
  1180.                     end;
  1181.             tall: 
  1182.                 if gSAT.wind^.portRect.bottom < gSAT.wind^.portRect.right then
  1183.                     begin
  1184.                         CheckItem(shapeMenu, tall, true);
  1185.                         CheckItem(shapeMenu, wide, false);
  1186.                         SizeWindow(gSAT.wind, p.v, p.h, false); {swap size}
  1187.                         KillSAT;
  1188.                         SetupSAT(gSAT.wind);
  1189.                         gameRunning := false;
  1190.                     end;
  1191.             otherwise
  1192.                 SysBeep(1);
  1193.         end;{case}
  1194.     end;
  1195.  
  1196.     procedure SetUpMenus;
  1197.     begin
  1198.         SkelApple('About CollisionIII…', @DoAbout);
  1199.         fileMenu := GetMenu(fileMenuRes);
  1200.         if fileMenu = nil then
  1201.             Barf;
  1202.         if SkelMenu(fileMenu, @DoFileMenu, nil, true) then
  1203.             ;
  1204.         shapeMenu := GetMenu(shapeMenuRes);
  1205.         if shapeMenu = nil then
  1206.             Barf;
  1207.         if SkelHMenu(shapeMenu, @DoShapeMenu, nil) then {Install as hierarcical menu}
  1208.             ;
  1209.         CheckItem(shapeMenu, 1, true); {Check "wide"}
  1210.     end;
  1211.  
  1212. begin
  1213.     SkelInit(6, nil);
  1214.     SkelSetSleep(0); {Tell TransSkel that we want attention as often as possible.}
  1215.     SetupMenus;
  1216.     SetupWindow;
  1217.     InitHigh;
  1218.     InitSpriteFaces;
  1219.     InitScoreFace;
  1220.     InitPlayerFaces;
  1221.     SkelBackground(@DirtyWork);
  1222.     lastTime := TickCount;
  1223.     randSeed := TickCount;
  1224.  
  1225.     Synth; {Build sounds!}
  1226.     SATSoundShutup;
  1227.  
  1228.     SkelMain;
  1229.     SkelClobber;
  1230.     SATSoundShutup;
  1231. end.